/*
 * PhotonRTOS础光实时操作系统 -- ActivateTask接口实现文件
 *
 * Copyright (C) 2022 国科础石(重庆)软件有限公司
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <autosar/internal.h>

VAR(volatile osek_system_state, AUTOMATIC) osek_system = OSEK_SYSTEM_INITIALIZED;

/**
 * 该服务返回当前应用模式。它可用于编写与模式相关的代码。
 * 语法：
 *     AppModeType GetActiveApplicationMode ( void )
 * 参数（输入）：
 *     无
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     应用模式主要用于测试、生产等不同场景。
 *     允许在任务、ISR以及所有钩子例程中调用。
 * 状态：
 *     标准：
 *        无
 *     扩展：
 *        无
 * 一致性：
 *        BCC1, BCC2, ECC1, ECC2
 */
FUNC(AppModeType, OS_CODE) GetActiveApplicationMode(void)
{
	CONTEXT_CHECK(ENV_TASK | ENV_CAT2_ISR | ENV_ERROR_HOOK \
		| ENV_PRETASK_HOOK | ENV_POSTTASK_HOOK | ENV_STARTUP_HOOK \
		| ENV_SHUTDOWN_HOOK)
	return autosar_get_app_mode(autosar_get_core_id());
}

/**
 * 用户可以调用该系统服务以特定模式启动操作系统。
 * 语法：
 *     void StartOS ( AppModeType <Mode> )
 * 参数（输入）：
 *     mode				应用模式
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     仅允许在操作系统之外使用，
 *     因此可能会实施特定的实现限制。
 * 状态：
 *     标准：
 *        无
 *     扩展：
 *        无
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 *
 * [SWS_Os_00607] StartOS应在调用它的核心上启动操作系统。
 * [SWS_Os_00424] 第一次调用StartOS() （用于启动操作系统）不应返回。
 * [SWS_Os_00585] 在调用StartOS之前和之后应该可以激活不受
 * AUTOSAR OS 控制的核心。
 */
FUNC(void, OS_CODE) StartOS(
    VAR(AppModeType, AUTOMATIC) Mode)
{
	VAR(int32_t, AUTOMATIC) i;
	VAR(int32_t, AUTOMATIC) core_id;

	VAR(static uintptr_t, AUTOMATIC)  wait_data[2] = {0, 0};

	/**
	 * 除了启动阶段外，任意场景均不可执行此函数
	*/
	CONTEXT_CHECK_IN_BOOT(ENV_MASK)
	os_run_context_init();
	if (Mode >= APPMODECOUNT) {
		pr_err("invalid app mode %d!\n", Mode);
		ShutdownOS(E_OS_VALUE);

		return;
	}

	/**
	 * StartOS函数应在已被 StartCore 激活的所有核心上调用。
	 * 不允许从已经调用 StartOS 的核心调用StartCore 。
	 */
	core_id = autosar_get_core_id();

	pr_debug("on core %d\n", core_id);

	if (autosar_get_core_status(core_id) != AUTOSAR_CORE) {
		pr_err("non autosar core called StartOS!\n");
		return;
	}

	/**
	 * 标记当前核已经启动OS
	 */
	cpumask_set_cpu(core_id, &autosar_startos_core);

	autosar_set_app_mode(core_id, Mode);

	/**
	 * 关闭所有2类中断
	 */
	disable_irq();

	if (core_id == 0) {
		DeclareCounter((AUTOSAR_NR_COUNTER - 1), 1024, 16, 1, SOFTWARE);
	}

	DeclareCounter(core_id, 0xFFFF, 10, 1, HARDWARE);

	if (osek_system == OSEK_SYSTEM_INITIALIZED) {
		osek_system = OSEK_SYSTEM_STARTING;
		/* 第一次启动OS，需要进行初始化 */
		/**
		 * 初始化autosar的 smp_spin_lock
		 */
		autosar_init_smp_spin_lock();
		/**
		 * [SWS_Os_00611] 如果配置了 IOC， StartOS将初始化 IOC 的数据结构。
		 */
#ifdef CONFIG_IOC
		config_to_ioc();
		IocInit();
#endif
		for (i = 0; i <OS_NR_TASK; i++) {
			if ((osek_task_attrs[i] != NULL)
					&& (osek_task_attrs[i]->entry != NULL)) {
				attrs_to_task(i);
			}
		}

		for (i = 0; i <OS_NR_ALARM; i++) {
			if ((osek_alarm_attrs[i] != NULL)) {
				osek_alarms[i].activated = 0;
				attrs_to_alarm(i);
			}
		}
	}

	/**
	 * 第一个同步点，等待其它核初始化完成
	 * [SWS_Os_00580] ⌈在调用全局StartupHook之前，所有属于
	 *  AUTOSAR 系统的核心都应在 StartOS 内同步。
	 */
	autosar_sync_all_cores(&wait_data[0], AUTOSAR_CORES);
	pr_debug("first time core %d sync done\n", core_id);

	/**
	 * 将当前模式下的任务添加OS调度表中
	 */
	for (i = 0; i <OS_NR_TASK; i++) {
		if (osek_tasks[i].entry != NULL
				&& osek_tasks[i].core_id == core_id) {
			osek_init_task(i);
		}
	}

	check_app_mode();

#if defined(AUTOSAR_OS_APPLICATION)
	application_init();
	trusted_func_init();
#endif /* defined(AUTOSAR_OS_APPLICATION) */

	/**
	 * 调用OS executes StartupHook钩子函数
	 */
#if defined(CONFIG_OSEK_HOOK_STARTUP)
	CONTEXT_MARK_START(ENV_STARTUP_HOOK)
	StartupHook();
	CONTEXT_MARK_END
#endif

	if (osek_system == OSEK_SYSTEM_STARTING) {
		osek_system = OSEK_SYSTEM_STARTED;
	}

	/**
	 * 调用application StartHook钩子函数
	 */
#if defined(CONFIG_AUTOSAR_APPLICATION_HOOK_STARTUP)
	CONTEXT_MARK_START(ENV_STARTUP_HOOK)
	os_app_startup_hooks();
	CONTEXT_MARK_END
#endif

	/**
	 * 第二个同步点，等待其它核调用钩子函数完成
	 * [SWS_Os_00579] 属于AUTOSAR系统的所有核在启动调度之前
	 * 和调用全局StartupHook之后都需要在StartOS函数内同步。
	 */
	autosar_sync_all_cores(&wait_data[1], AUTOSAR_CORES);
	pr_debug("second time core %d sync done\n", core_id);

	/**
	 * 将当前模式下的任务添加OS调度表中
	 */
	for (i = 0; i <OS_NR_TASK; i++) {
		if ((osek_tasks[i].entry != NULL)
			&& (osek_tasks[i].auto_start)
			&& (osek_tasks[i].core_id == core_id)
			&& (osek_tasks[i].app_mode == autosar_get_app_mode(core_id))) {
				ActivateTask(i);
		}
	}
	/**
	 * 系统开始运行，调度任务
	 */
	enable_irq();
	pr_debug("start core %d end\n", core_id);
	/**
	 * StartOS不需要返回，为了节省资源
	 */
	cpu_idle();

	/**
	 * 此处不应该返回
	 */
	panic("StartOS should never return!");
}

/**
 *  用户可以调用该系统服务以终止整个系统（例如紧急关机）。
 * 操作系统也在内部调用该函数，
 * 如果它到达未定义内部状态并且不再准备运行。
 *  如果ShutdownHook被配置，
 * ShutdownHook例程在关闭操作系统前
 * 总是被调用（以<Error>作为参数）。
 *  如果ShutdownHook返回，更多ShutdownOS的行为是实现特定的。
 * 在OSEK OS和OSEKtime OS共存时，ShutdownHook必须返回。
 *  <Error>必须是一个OSEK OS支持的有效错误代码，
 * 在OSEK OS和OSEKtime OS共存时，
 * <Error>也可以是OSEKtime OS所接受的值。
 * 在这种情况下，如果被OSEKtime配置参数打开，
 * OSEKtime OS将在OSEK OS关闭后关闭。
 * 语法：
 *     void ShutdownOS ( StatusType <Error> )
 * 参数（输入）：
 *     Error				错误发生
 * 参数（输出）：
 *     无
 * 特殊说明：
 *     在该服务调用后，操作系统被关闭。
 *     允许任务、ISR、ErrorHook、StartupHook以及
 *     在操作系统内部调用。
 * 状态：
 *     标准：
 *        无
 *     扩展：
 *        无
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */
FUNC(void, OS_CODE) ShutdownOS(
	VAR(StatusType, AUTOMATIC) Error)
{

	CONTEXT_CHECK_NO_RETURN_VAL(ENV_TASK | ENV_CAT2_ISR | ENV_ERROR_HOOK | ENV_STARTUP_HOOK)
#if defined(AUTOSAR_OS_APPLICATION)
	/**
	 * [SWS_Os_00054] 如果是不受信任的os-app则忽略其shutdown请求。
	*/
	if (autosar_os_app[GetApplicationID()].if_trusted == false) {
		return;
	}
#endif
	autosar_shutdown_os(Error);
}

/**
 * 返回当前系统日志缓冲区信息。
 * 语法：
 *     StatusType GetSysLog ( void )
 * 参数（输入）：
 *     缓冲区，缓冲区大小，清除系统缓冲区标志
 * 参数（输出）：
 *     系统日志
 * 特殊说明：
 *     允许在任务级、ISR级以及内部钩子例程调用。
 *     该服务目的是用在库函数和钩子例程中。
 * 状态：
 *     标准：
 *        没有错误，E_OK
 *        复制失败，E_OS_VALUE
 *     扩展：
 *        没有错误，E_OK
 *        复制失败，E_OS_VALUE
 * 一致性：
 *     BCC1, BCC2, ECC1, ECC2
 */

FUNC(StatusType, OS_CODE) GetSysLog(
	VAR(int8, AUTOMATIC) buf[],
	VAR(uint32, AUTOMATIC) size,
	VAR(boolean, AUTOMATIC) buf_clear)
{
	VAR(StatusType, AUTOMATIC) ret = E_OK;
	VAR(uint32, AUTOMATIC) count = dmesg(buf, size, buf_clear);
	/* 如果写入缓冲区的大小为0，那么说明写入失败 */
	if (!count) {
		ret = E_OS_VALUE;
	}
	return ret;
}
